package com.lhkj.iams.framework.exception;

import com.google.common.collect.Sets;
import com.lhkj.iams.base.enums.ReturnStatus;
import com.lhkj.iams.base.ro.ActionResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.omg.CORBA.SystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 全局异常处理
 * </p>
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public ActionResult<Void> defaultExceptionHandler(Exception e) {
        logger.error("Exception异常", e);
        return buildStackTraceResult(ReturnStatus.SC_INTERNAL_SERVER_ERROR.getCode(), e);
    }

    @ExceptionHandler(SystemException.class)
    public ActionResult<Void> defaultServiceExceptionHandler(SystemException e) {
        logger.error("ServiceException异常", e);
        return buildErrorMessageResult(ReturnStatus.SC_INTERNAL_SERVER_ERROR.getCode(), e);
    }

    /**
     * 如果在Controller层中已经声明过BindingResult来获取信息则不会再抛出BindException，
     * 如果不在具体的Controller获取信息，可以统一通过捕获BindException处理BindingResult中的信息；
     * hibernate 参数校验异常（get)
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public ActionResult<Void> defaultConstraintViolationException(ConstraintViolationException e) {
        Set<String> errors = Sets.newHashSet();
        for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
            System.out.println(constraintViolation.getMessage());
            errors.add(constraintViolation.getMessage());
        }
        logger.error("ConstraintViolationException异常：" + e.getMessage());
        return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), StringUtils.defaultIfBlank(String.join("\r", errors), "参数验证失败"));
    }

    /**
     * 如果在Controller层中已经声明过BindingResult来获取信息则不会再抛出BindException，
     * 如果不在具体的Controller获取信息，可以统一通过捕获BindException处理BindingResult中的信息；
     * hibernate 参数校验异常（get)
     */
    @ExceptionHandler({BindException.class})
    public ActionResult<Void> defaultBindException(BindException e) {
        logger.error("BindException异常：" + e.getMessage());
        if (e.hasErrors()) {
//            String errorMsg = e.getFieldErrors().stream().map(fieldError -> String.format("%s[%s]", fieldError.getDefaultMessage(), fieldError.getField())).collect(Collectors.joining("\r"));
            String errorMsg = e.getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining("\r"));
            return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), errorMsg);
        }
        return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e.getMessage());
    }

    /**
     * hibernate 参数校验异常(post)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ActionResult<Void> defaultMethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        logger.error("MethodArgumentNotValidException异常", e);
        Set<String> errors = Sets.newHashSet();
        for (FieldError error : e.getBindingResult().getFieldErrors()) {
            errors.add(error.getDefaultMessage());
        }
        return buildResult(ReturnStatus.SC_BAD_REQUEST.getCode(), String.join("\r", errors));
    }

    /**
     * get请求 @RequestParam校验 require = true
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ActionResult<Void> defaultMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        logger.error("MissingServletRequestParameterException异常：" + ExceptionUtils.getStackTrace(e));
        return buildErrorMessageResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e);
    }

    /**
     * 处理 Http消息不可读异常，如GET请求不能使用@RequestBody
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ActionResult<Void> defaultHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        logger.error("HttpMessageNotReadableException异常：", e);
        return buildErrorMessageResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e);
    }

    /**
     * 404 找不到对应资源
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ActionResult<Void> noHandlerFoundException(NoHandlerFoundException e) {
        logger.error("NoHandlerFoundException异常：" + org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e));
        return buildResult(ReturnStatus.SC_NOT_FOUND.getCode(), "找不到对应资源");
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ActionResult<Void> defaultIllegalArgumentExceptionHandler(IllegalArgumentException e) {
        logger.error("IllegalArgumentException异常", e);
        return buildStackTraceResult(ReturnStatus.SC_BAD_REQUEST.getCode(), e);
    }

    /**
     * 构建异常返回值，抓取堆栈异常
     */
    private ActionResult<Void> buildStackTraceResult(int errorCode, Exception e) {
        return buildResult(errorCode, ExceptionUtils.getStackTrace(e));
    }

    /**
     * 构建异常返回值，抓取堆栈异常
     */
    private ActionResult<Void> buildErrorMessageResult(int errorCode, Exception e) {
        return buildResult(errorCode, e.getMessage());
    }

    /**
     * 构建异常返回值,自定义异常信息
     */
    private ActionResult<Void> buildResult(int errorCode, String msg) {
        return ActionResult.fail(errorCode, msg);
    }

}
